/* 
 *	Copyright (C) 2007 cooleyes
 *	eyes.cooleyes@gmail.com 
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */
 
#include "flac_parser.h"
#include "common/mem64.h"
#include <FLAC/stream_decoder.h>
#include <string.h>

#define SIRENS2_FLAC_SAMPLES_PER_FRAME 2048

typedef struct {
	buffered_reader_t* reader;
	FLAC__StreamDecoder* decoder;
	void* output_buffer;
	uint32_t cache_samples;
	uint32_t sample_bits;
	uint32_t channels;
	uint32_t samplerate;
	uint32_t total_samples;
} FlacParserPrivateData;


static FLAC__StreamDecoderReadStatus stream_decoder_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data) {
	
	(void)decoder;
	FlacParserPrivateData* priv_data = (FlacParserPrivateData*)client_data;
	
	const unsigned requested_bytes = *bytes;
	if ( requested_bytes > 0 ) {
		*bytes = buffered_reader_read(priv_data->reader, buffer, *bytes);
		if ( *bytes == 0 )
			return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
		else
			return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
	}
	else
		return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
}

static FLAC__StreamDecoderSeekStatus stream_decoder_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) {
	
	(void)decoder;
	FlacParserPrivateData* priv_data = (FlacParserPrivateData*)client_data;

	if(buffered_reader_seek(priv_data->reader, (int32_t)absolute_byte_offset) != (int32_t)absolute_byte_offset) {
		return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
	}
	return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
}

static FLAC__StreamDecoderTellStatus stream_decoder_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) {

	(void)decoder;
	FlacParserPrivateData* priv_data = (FlacParserPrivateData*)client_data;

	*absolute_byte_offset = (FLAC__uint64)buffered_reader_position(priv_data->reader);

	return FLAC__STREAM_DECODER_TELL_STATUS_OK;
}

static FLAC__StreamDecoderLengthStatus stream_decoder_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) {
	
	(void)decoder;
	FlacParserPrivateData* priv_data = (FlacParserPrivateData*)client_data;

	*stream_length = (FLAC__uint64)buffered_reader_length(priv_data->reader);
	
	return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}

static FLAC__bool stream_decoder_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data) {
	
	(void)decoder;
	FlacParserPrivateData* priv_data = (FlacParserPrivateData*)client_data;

	return ( buffered_reader_length(priv_data->reader) == buffered_reader_position(priv_data->reader) );
}

static void stream_decoder_metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) {
	
	(void)decoder;
	FlacParserPrivateData* priv_data = (FlacParserPrivateData*)client_data;
	
	if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
		priv_data->samplerate = metadata->data.stream_info.sample_rate;
		priv_data->channels = metadata->data.stream_info.channels;
		priv_data->total_samples = metadata->data.stream_info.total_samples & 0xffffffff;
		priv_data->sample_bits = metadata->data.stream_info.bits_per_sample;
	}
	
}

static FLAC__StreamDecoderWriteStatus stream_decoder_write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
{
	(void)decoder;
	FlacParserPrivateData* priv_data = (FlacParserPrivateData*)client_data;

	unsigned samples = frame->header.blocksize;
	unsigned i;
	
	memset(priv_data->output_buffer, 0, FLAC__MAX_BLOCK_SIZE * 4);
	uint16_t* output_buffer = (uint16_t*)priv_data->output_buffer;
	if ( priv_data->channels == 2 ) {
		for(i = 0; i < samples; i++) {
			output_buffer[2*i] = (uint16_t)buffer[0][i];
			output_buffer[2*i+1] = (uint16_t)buffer[1][i];
		}
	}
	else {
		for(i = 0; i < samples; i++) {		
			output_buffer[2*i] = (uint16_t)buffer[0][i];
			output_buffer[2*i+1] = (uint16_t)buffer[0][i];	
		}
	}
	
	priv_data->cache_samples = samples;
	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}

static void stream_decoder_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) {
	
}

FlacParser::FlacParser() {
	initialize();
};

FlacParser::~FlacParser() {
	close();
};

void FlacParser::initialize() {
	is_open = false;
	
	total_samples = 0;
	
	reader = 0;
	priv_data = 0;
	
	memset(&init_data, 0, sizeof(AudioCodecInitData));
}

void FlacParser::finalize() {
	if ( priv_data ) {
		FlacParserPrivateData* priv = (FlacParserPrivateData*)priv_data;
		if ( priv->output_buffer )
			free_64(priv->output_buffer);
		if ( priv->decoder )
			FLAC__stream_decoder_delete(priv->decoder);
		free_64(priv_data);
	}
	if ( reader ) {
		buffered_reader_close(reader);
	}
	initialize();
};


char* FlacParser::open(const char* filename) {
	
	close();
	
	reader = buffered_reader_open(filename, 262144, 0);
	if ( !reader ) {
		finalize();
		return "FlacParser : open buffered_reader fail";
	}
	buffered_reader_seek(reader, 0);	
	
	priv_data = malloc_64(sizeof(FlacParserPrivateData));
	if ( priv_data == 0 ) {
		finalize();
		return "FlacParser : malloc priv_data fail";
	}
	memset(priv_data, 0, sizeof(FlacParserPrivateData));
	
	FlacParserPrivateData* priv = (FlacParserPrivateData*)priv_data;
	priv->reader = reader;
	
	priv->output_buffer = malloc_64(FLAC__MAX_BLOCK_SIZE * 4);
	if (priv->output_buffer == 0) {
		finalize();
		return "FlacParser : malloc priv_output_buffer fail";
	}
	
	priv->decoder = FLAC__stream_decoder_new();
	if ( priv->decoder == 0 ) {
		finalize();
		return "FlacParser : open FLAC_Stream_Decoder fail";
	}
	FLAC__StreamDecoderInitStatus init_status;
	
	init_status = FLAC__stream_decoder_init_stream(priv->decoder, 
				stream_decoder_read_callback_, 
				stream_decoder_seek_callback_, 
				stream_decoder_tell_callback_, 
				stream_decoder_length_callback_, 
				stream_decoder_eof_callback_, 
				stream_decoder_write_callback_, 
				stream_decoder_metadata_callback_, 
				stream_decoder_error_callback_, 
				priv_data);
	if ( init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK ) {
		finalize();
		return "FlacParser : init FLAC_Stream_Decoder fail";
	}
	
	if ( ! FLAC__stream_decoder_process_until_end_of_metadata(priv->decoder) ) {
		finalize();
		return "FlacParser : parser FLAC metadata fail";
	}
	
	init_data.channels = priv->channels;
	if ( init_data.channels < 0 || init_data.channels > 2 ) {
		finalize();
		return "FlacParser : channels != 1 && channels != 2";
	}
	init_data.samplerate = priv->samplerate;
	init_data.sample_bits = priv->sample_bits;
	if ( init_data.sample_bits != 16 ) {
		finalize();
		return "FlacParser : sample bits != 16";
	}
	init_data.samples_per_frame = SIRENS2_FLAC_SAMPLES_PER_FRAME;
	init_data.samples_per_decoded = init_data.samples_per_frame;
	init_data.frame_align = init_data.samples_per_frame * (16 >> 3) * 2;
	total_samples = priv->total_samples;
	
	is_open = true;
	return 0;
};

void FlacParser::close() {
	if ( is_open ) {
		finalize();
	}
};

uint32_t FlacParser::get_samplerate() {
	return init_data.samplerate;
};

uint32_t FlacParser::get_channels() {
	return init_data.channels;
};

uint64_t FlacParser::get_duration() {
	uint64_t duration = 1000000LL*total_samples/init_data.samplerate;
	return duration;
};
	
uint32_t FlacParser::get_samples_per_frame() {
	return init_data.samples_per_frame;
};

uint32_t FlacParser::get_max_samples_per_frame() {
	return FLAC__MAX_BLOCK_SIZE;
};

uint64_t FlacParser::seek_time(uint64_t time) {
	uint32_t samples = (uint32_t)(time*init_data.samplerate/1000000LL);
	if ( samples > total_samples )
		samples = total_samples;
	FlacParserPrivateData* priv = (FlacParserPrivateData*)priv_data;
	FLAC__stream_decoder_seek_absolute(priv->decoder, samples);
	
	uint64_t current = 1000000LL*samples/init_data.samplerate;
	return current;
};

char* FlacParser::get_frame(void** pcm, uint32_t* samples) {
	
	if( !is_open )
		return "FlacParser : parser is not open";
	
	FlacParserPrivateData* priv = (FlacParserPrivateData*)priv_data;
	
	FLAC__stream_decoder_process_single(priv->decoder);
	*pcm = priv->output_buffer;
	*samples = priv->cache_samples;
	priv->cache_samples = 0;
	return 0;
};





